# - Find Thrift (a cross platform RPC lib/tool)
# This module defines
#  THRIFT_VERSION_STRING, version string of ant if found
#  THRIFT_LIBRARIES, static or shared libraries to link
#  THRIFT_LIBRARIES_SHARED ,true if shared,otherwise false
#  THRIFT_LIBRARIES_BUILD_TYPE , 'RELEASE' or 'DEBUG'
#  THRIFT_INCLUDE_DIR, where to find THRIFT headers
#  THRIFT_COMPILER, thrift compiler executable
#  THRIFT_FOUND, If false, do not try to use ant
#　THRIFT_LIBRARY_STATIC static library to link
#
# Imported target:
#  thrift 
#  thrift-static,same with target 'thrift' if target 'thrift' is static
#
# Function:
#  thrift_gen_cpp(<path to thrift file> <output variable with file list>)
# 
# Variablies to set:
# USE_THRIFT_WITH_MT, whether use thrift library build with /MT option
#
# Initial work was done by Cloudera https://github.com/cloudera/Impala
# 2014 - modified by snikulov
# 2018 - modified by guyadong

# FindBoost.cmake supported imported targets after cmake version 3.5
cmake_minimum_required( VERSION 3.5 )

# prefer the thrift version supplied in THRIFT_HOME (cmake -DTHRIFT_HOME then environment)
find_path(THRIFT_INCLUDE_DIR
    NAMES
        thrift/Thrift.h
    HINTS
        ${THRIFT_HOME}
        ENV THRIFT_HOME
        /usr/local
        /opt/local
    PATH_SUFFIXES
        include
)
if(MSVC)
	option(USE_THRIFT_WITH_MT "specify whether find and use thrift library build with 'WITH_MT' option"  ON)
endif()
option(USE_THRIFT_WITH_BOOSTTHREADS "specify whether the thrift library build with 'WITH_BOOSTTHREADS' option" ON)
# 指定搜索的库名结尾符
set(THRIFT_STATIC_POSTFIX "")

if(MSVC)
	if(USE_THRIFT_WITH_MT)
		set(THRIFT_STATIC_POSTFIX "mt")
	else()
		set(THRIFT_STATIC_POSTFIX "md")
	endif()
endif(MSVC)
# debug版结尾
set(THRIFT_DEBUG_POSTFIX "d")

macro(FIND_THRIFT_LIBRARY var)
	# prefer the thrift version supplied in THRIFT_HOME
	find_library(${var}
	    NAMES
	        ${ARGN}
	    HINTS
	        ${THRIFT_HOME}
	        ENV THRIFT_HOME
	        /usr/local
	        /opt/local
	    PATH_SUFFIXES
	        lib lib64
	)
endmacro(FIND_THRIFT_LIBRARY)
# 根据library name判断是否为debug版
# 创建变量 ${lib_var}_BUILD_TYPE 保存结果,是debug版则为DEBUG否则为RELEASE
macro(THRIFT_BUILD_TYPE lib_var)
	if(NOT DEFINED ${lib_var})
		message(FATAL_ERROR "not defined lib_var:${lib_var}")
	endif()
	if(NOT EXISTS "${${lib_var}}")
		message(FATAL_ERROR "not exists file:${${lib_var}}")
	endif()	
	get_filename_component(_name ${${lib_var}} NAME_WE)
	if(_name MATCHES "thrift${THRIFT_STATIC_POSTFIX}${THRIFT_DEBUG_POSTFIX}")
		set(${lib_var}_BUILD_TYPE "DEBUG")
	else()
		set(${lib_var}_BUILD_TYPE "RELEASE")
	endif()
	unset(_name)
endmacro(THRIFT_BUILD_TYPE)
# 根据已有的库lib_var,查找debug版库,
# 创建变量${lib_var}_DEBUG保存结果,没有找到则值为${lib_var}_DEBUG-NOTFOUND
macro(FIND_THRIFT_DEBUG_LIBRARY lib_var)
	if(NOT DEFINED ${lib_var})
		message(FATAL_ERROR "not defined lib_var:${lib_var}")
	endif()
	if(NOT EXISTS "${${lib_var}}")
		message(FATAL_ERROR "not exists file:${${lib_var}}")
	endif()
	THRIFT_BUILD_TYPE(${lib_var})	
	IF(${lib_var}_BUILD_TYPE STREQUAL "RELEASE")
		get_filename_component(_name ${${lib_var}} NAME_WE)
		get_filename_component(_ext ${${lib_var}} EXT)
		get_filename_component(_path ${${lib_var}} PATH)
		find_file(${lib_var}_DEBUG ${_name}${THRIFT_DEBUG_POSTFIX}${_ext} 
			PATHS ${_path} 
			NO_DEFAULT_PATH)
		unset(_name)
		unset(_ext)
		unset(_path)
	endif()
endmacro(FIND_THRIFT_DEBUG_LIBRARY)

# 根据文件名后缀判断是否为动态库
# 创建${lib_var}_SHARED 变量，为true则是动态库，false为静态库，其他类型则报错
macro(THRIFT_SHARED_LIBRARY lib_var)
	if(NOT DEFINED ${lib_var})
		message(FATAL_ERROR "not defined lib_var:${lib_var}")
	endif()
	get_filename_component(_ext ${${lib_var}} EXT)
	get_filename_component(_name ${${lib_var}} NAME)
	if(MSVC AND _name MATCHES "m[dt]" AND "${_ext}" STREQUAL "${CMAKE_IMPORT_LIBRARY_SUFFIX}")
		set(${lib_var}_SHARED FALSE)
	elseif("${_ext}" STREQUAL "${CMAKE_IMPORT_LIBRARY_SUFFIX}")
		set(${lib_var}_SHARED TRUE)
	elseif("${_ext}" STREQUAL "${CMAKE_STATIC_LIBRARY_SUFFIX}")
		set(${lib_var}_SHARED FALSE)
	elseif("${_ext}" STREQUAL "${CMAKE_SHARED_LIBRARY_SUFFIX}")
		set(${lib_var}_SHARED TRUE)
	else()
		message(FATAL_ERROR "not library file:${${lib_var}}")
	endif()	
	unset(_ext)
	unset(_name)
endmacro(THRIFT_SHARED_LIBRARY)

# 查找thrift依赖库 boost
# 如果相关依赖库找不到则报错退出
macro(FIND_THRIFT_DEPENDENCIES)
	# thrift编译时指定使用静态库
	# 参见 https://github.com/10km/thrift/blob/0.11.0-patch/build/wincpp/build-thrift.bat
	set(Boost_USE_STATIC_LIBS ON)
	set(Boost_USE_STATIC_RUNTIME ${USE_THRIFT_WITH_MT})
	
	set(BOOST_REQUIRED_COMPONENTS)
	if(USE_THRIFT_WITH_BOOSTTHREADS)
		list(APPEND BOOST_REQUIRED_COMPONENTS system thread)
	endif()
	find_package(Boost 1.62 REQUIRED COMPONENTS ${BOOST_REQUIRED_COMPONENTS})
	# 依赖库的include文件夹
	set(THRIFT_DEPENDENT_DIRS)

	# 依赖的库
	set(THRIFT_DEPENDENT_LIBRARIES)
	foreach(_component ${BOOST_REQUIRED_COMPONENTS})
		# boost imported target
		list(APPEND THRIFT_DEPENDENT_LIBRARIES Boost::${_component})
	endforeach()
endmacro(FIND_THRIFT_DEPENDENCIES)

# prefer default thrift library
FIND_THRIFT_LIBRARY(THRIFT_LIBRARIES thrift thrift${THRIFT_STATIC_POSTFIX} thrift${THRIFT_STATIC_POSTFIX}${THRIFT_DEBUG_POSTFIX})

find_program(THRIFT_COMPILER
    NAMES
        thrift
    HINTS
        ${THRIFT_HOME}
        ENV THRIFT_HOME
        /usr/local
        /opt/local
    PATH_SUFFIXES
        bin bin64
)

if (THRIFT_COMPILER)
    exec_program(${THRIFT_COMPILER}
        ARGS -version OUTPUT_VARIABLE __thrift_OUT RETURN_VALUE THRIFT_RETURN)
    string(REGEX MATCH "[0-9]+.[0-9]+.[0-9]+-[a-z]+$" THRIFT_VERSION_STRING ${__thrift_OUT})

    # define utility function to generate cpp files
    function(thrift_gen_cpp thrift_file THRIFT_CPP_FILES_LIST THRIFT_GEN_INCLUDE_DIR)
        set(_res)
        set(_res_inc_path)
        if(EXISTS ${thrift_file})
            get_filename_component(_target_dir ${thrift_file} NAME_WE)
            message("thrif_gen_cpp: ${thrift_file}")

            if(NOT EXISTS ${CMAKE_BINARY_DIR}/${_target_dir})
                file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/${_target_dir})
            endif()
            exec_program(${THRIFT_COMPILER}
                ARGS -o "${CMAKE_BINARY_DIR}/${_target_dir}" --gen cpp ${thrift_file}
                OUTPUT_VARIABLE __thrift_OUT
                RETURN_VALUE THRIFT_RETURN)
            file(GLOB_RECURSE __result_src "${CMAKE_BINARY_DIR}/${_target_dir}/*.cpp")
            file(GLOB_RECURSE __result_hdr "${CMAKE_BINARY_DIR}/${_target_dir}/*.h")
            list(APPEND _res ${__result_src})
            list(APPEND _res ${__result_hdr})
            if(__result_hdr)
                list(GET __result_hdr 0 _res_inc_path)
                get_filename_component(_res_inc_path ${_res_inc_path} DIRECTORY)
            endif()
        else()
            message("thrift_gen_cpp: file ${thrift_file} does not exists")
        endif()
        set(${THRIFT_CPP_FILES_LIST} "${_res}" PARENT_SCOPE)
        set(${THRIFT_GEN_INCLUDE_DIR} "${_res_inc_path}" PARENT_SCOPE)
    endfunction()
endif ()


include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(THRIFT DEFAULT_MSG THRIFT_LIBRARIES THRIFT_INCLUDE_DIR THRIFT_COMPILER)
mark_as_advanced(THRIFT_LIBRARIES THRIFT_INCLUDE_DIR THRIFT_COMPILER THRIFT_VERSION_STRING)

############################
function(set_thrift_imported_target target build_type imported_location)
	if(NOT TARGET ${target})
		message(FATAL_ERROR "${target} is invalid target")
	endif()
	if(NOT EXISTS ${imported_location})
		message(FATAL_ERROR "${imported_location} is invalid library")
	endif()
	set_property(TARGET ${target} APPEND PROPERTY IMPORTED_CONFIGURATIONS ${build_type})
	set_target_properties(${target} PROPERTIES
	  IMPORTED_LINK_INTERFACE_LANGUAGES_${build_type} "CXX"
	  INTERFACE_INCLUDE_DIRECTORIES "${THRIFT_INCLUDE_DIR};${THRIFT_DEPENDENT_DIRS}"
		IMPORTED_LINK_INTERFACE_LIBRARIES "${THRIFT_DEPENDENT_LIBRARIES};$<$<STREQUAL:${CMAKE_CXX_PLATFORM_ID},MinGW>:-lWs2_32>"
	  IMPORTED_LOCATION_${build_type} "${imported_location}"
	  )
endfunction(set_thrift_imported_target imported_location)

function(set_thrift_target_debug target var_shared)
	if( "${${var_shared}_BUILD_TYPE}" STREQUAL "RELEASE")
		FIND_THRIFT_DEBUG_LIBRARY(${var_shared})
		if(${var_shared}_DEBUG)
			set_thrift_imported_target(${target} DEBUG ${${var_shared}_DEBUG})
		endif()
	endif()
endfunction(set_thrift_target_debug)

if(THRIFT_FOUND)
	set(Thrift_FOUND TRUE)
	
	FIND_THRIFT_DEPENDENCIES()
	THRIFT_SHARED_LIBRARY(THRIFT_LIBRARIES)
		# Create imported target
	if(THRIFT_LIBRARIES_SHARED)
		add_library(thrift SHARED IMPORTED)
	else()
		add_library(thrift STATIC IMPORTED)
	endif()

	THRIFT_BUILD_TYPE(THRIFT_LIBRARIES)
	set_thrift_imported_target(thrift ${THRIFT_LIBRARIES_BUILD_TYPE} ${THRIFT_LIBRARIES})
	set_thrift_target_debug(thrift THRIFT_LIBRARIES)
	if(THRIFT_LIBRARIES_SHARED)
		## 查找静态库
		FIND_THRIFT_LIBRARY(THRIFT_LIBRARY_STATIC ${CMAKE_STATIC_LIBRARY_PREFIX}thrift${CMAKE_STATIC_LIBRARY_SUFFIX})
		if(THRIFT_LIBRARY_STATIC)
			add_library(thrift-static STATIC IMPORTED)
			THRIFT_BUILD_TYPE(THRIFT_LIBRARY_STATIC)
			set_thrift_imported_target(thrift-static ${THRIFT_LIBRARY_STATIC_BUILD_TYPE} ${THRIFT_LIBRARY_STATIC})
			set_thrift_target_debug(thrift-static THRIFT_LIBRARY_STATIC)
		endif()
	else()
		## thrift-static与thrift相同
		set(THRIFT_LIBRARY_STATIC ${THRIFT_LIBRARIES})
		add_library(thrift-static STATIC IMPORTED)
		set_thrift_imported_target(thrift-static ${THRIFT_LIBRARIES_BUILD_TYPE} ${THRIFT_LIBRARIES})
		set_thrift_target_debug(thrift-static THRIFT_LIBRARIES)
	endif()

endif()

unset(BOOST_REQUIRED_COMPONENTS)
unset (THRIFT_DEPENDENT_DIRS)
unset(THRIFT_DEPENDENT_LIBRARIES)